﻿using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Net.Sockets;
using System.Text;
using TCPBaseModbusTCP.Models;

namespace TCPBaseModbusTCP.Services;

public class TcpClientService : BackgroundService
{
    private readonly TcpClientSetting _settings;
    private TcpClient? _tcpClient;
    private NetworkStream? _networkStream;
    private bool _isConnected;
    private readonly object _lockObj = new();
    private readonly List<ReceivedGatewayData> _receivedGatewayData = new(); // 存储接收的网关数据
    private readonly CancellationTokenSource _cts = new();

    // 客户端状态信息
    public bool IsConnected => _isConnected;
    public string ClientId => _settings.ClientId;
    public IEnumerable<ReceivedGatewayData> ReceivedData
    {
        get
        {
            lock (_lockObj)
            {
                return _receivedGatewayData.ToList();
            }
        }
    }

    public TcpClientService(IOptions<TcpClientSetting> settings)
    {
        _settings = settings.Value;
    }

    /// <summary>
    /// 发送消息到服务端（供API调用）
    /// </summary>
    public async Task<string> SendMessageAsync(string message)
    {
        if (!_isConnected || _networkStream == null)
            return "ERROR: 未连接到服务端";

        try
        {
            // 发送客户端消息（自定义格式）
            byte[] data = Encoding.UTF8.GetBytes(message);
            await _networkStream.WriteAsync(data, 0, data.Length, _cts.Token);

            // 等待服务端响应（区分响应消息和网关转发数据）
            byte[] buffer = new byte[_settings.ReceiveBufferSize];
            int bytesRead = await _networkStream.ReadAsync(buffer, 0, buffer.Length, _cts.Token);

            if (bytesRead == 0)
                return "ERROR: 服务端已断开连接";
            //暂时固定只处理192.168.1.254的数据
            string gatewayIp = "192.168.1.254";
            // 判断是文本响应还是Modbus二进制数据
            if (IsTextMessage(buffer, bytesRead))
            {
                // 服务端的文本响应（如"CLIENT_ACK: ..."）
                return Encoding.UTF8.GetString(buffer, 0, bytesRead);
            }
            else
            {
                // 可能是网关转发的Modbus数据（存入接收列表，返回提示）
                HandleGatewayData(buffer, bytesRead,gatewayIp);
                return "已接收网关数据（详见ReceivedData）";
            }
        }
        catch (Exception ex)
        {
            return $"ERROR: {ex.Message}";
        }
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Console.WriteLine($"客户端启动（ID: {_settings.ClientId}），连接目标：{_settings.ServerIp}:{_settings.ServerPort}");

        // 链接两个取消令牌（外部停止 + 内部取消）
        using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, _cts.Token);

        while (!linkedCts.Token.IsCancellationRequested)
        {
            if (!_isConnected)
            {
                // 未连接状态：尝试连接并握手
                await TryConnectAndHandshakeAsync(linkedCts.Token);
                await Task.Delay(1000, linkedCts.Token);
            }
            else
            {
                // 已连接状态：持续监听服务端推送的消息（尤其是网关数据）
                await ListenForServerMessagesAsync(linkedCts.Token);
            }
        }

        // 清理资源
        Disconnect();
        Console.WriteLine("客户端已停止");
    }

    /// <summary>
    /// 尝试连接服务端并完成身份验证
    /// </summary>
    private async Task TryConnectAndHandshakeAsync(CancellationToken cancellationToken)
    {
        try
        {
            _tcpClient = new TcpClient();
            await _tcpClient.ConnectAsync(_settings.ServerIp, _settings.ServerPort, cancellationToken);
            _networkStream = _tcpClient.GetStream();

            // 发送身份标识（格式：CLIENT_ID:xxx）
            string handshake = $"CLIENT_ID:{_settings.ClientId}";
            byte[] handshakeData = Encoding.UTF8.GetBytes(handshake);
            await _networkStream.WriteAsync(handshakeData, 0, handshakeData.Length, cancellationToken);
            Console.WriteLine($"已发送身份标识：{handshake}");

            // 等待服务端验证响应
            byte[] responseBuffer = new byte[_settings.ReceiveBufferSize];
            int bytesRead = await _networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length, cancellationToken);
            string response = Encoding.UTF8.GetString(responseBuffer, 0, bytesRead);

            if (response.StartsWith("ACK:"))
            {
                _isConnected = true;
                Console.WriteLine($"连接成功：{response}");
            }
            else
            {
                Console.WriteLine($"握手失败：{response}");
                Disconnect();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接失败：{ex.Message}，{_settings.ReconnectIntervalMs}ms后重试");
            Disconnect();
            await Task.Delay(_settings.ReconnectIntervalMs, cancellationToken);
        }
    }

    /// <summary>
    /// 持续监听服务端发送的消息（包括网关转发数据）
    /// </summary>
    private async Task ListenForServerMessagesAsync(CancellationToken cancellationToken)
    {
        if (_networkStream == null || !_tcpClient!.Connected)
        {
            _isConnected = false;
            return;
        }

        try
        {
            byte[] buffer = new byte[_settings.ReceiveBufferSize];
            int bytesRead = await _networkStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);

            if (bytesRead == 0)
            {
                Console.WriteLine("服务端已断开连接");
                _isConnected = false;
                return;
            }

            // 从连接信息中获取网关IP（实际应从服务端转发的元数据中获取，此处简化为从连接端点解析）
            // 注意：实际场景中服务端应在转发时携带网关IP，此处为临时方案
            string gatewayIp = "192.168.1.254"; // 临时固定，实际需从服务端获取

            if (IsTextMessage(buffer, bytesRead))
            {
                string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                Console.WriteLine($"收到服务端消息：{message}");
            }
            else
            {
                // 调用修改后的HandleGatewayData，传入网关IP
                HandleGatewayData(buffer, bytesRead, gatewayIp);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"接收消息异常：{ex.Message}");
            _isConnected = false;
        }
    }

    /// <summary>
    /// 处理网关数据（解析Modbus格式并存储）
    /// </summary>
    private void HandleGatewayData(byte[] data, int length, string gatewayIp)
    {
        try
        {
            // 仅处理IP为192.168.1.254的网关数据
            if (gatewayIp != "192.168.1.254")
            {
                Console.WriteLine($"忽略非目标网关({gatewayIp})的数据（仅处理192.168.1.254）");
                return;
            }

            // 复制原始数据
            byte[] rawData = new byte[length];
            Array.Copy(data, rawData, length);

            // 解析Modbus数据包
            var (isValid, packet, _) = ParseModbusPacket(data, length);
            if (!isValid || packet == null)
            {
                Console.WriteLine($"网关({gatewayIp})数据格式无效，跳过处理");
                return;
            }

            // 临时寄存器地址映射（仅针对192.168.1.254）
            // 键：寄存器地址，值：(测点名称, 单位, 缩放因子)
            var tempAddressMap = new Dictionary<int, (string PointName, string Unit, double Scale)>
        {
            { 0, ("Humidity", "%", 0.1) },   // 地址0 → 湿度，原始值×0.1
            { 1, ("Temperature", "℃", 0.1) } // 地址1 → 温度，原始值×0.1
        };

            // 解析功能码0x03（读取保持寄存器）的响应数据
            if (packet.FunctionCode == 0x03)
            {
                // 假设请求的起始地址从0开始（实际应从请求报文中解析，此处简化）
                int startAddress = 0;
                int byteIndex = 0;

                // 按2字节（1个寄存器）解析数据
                while (byteIndex + 1 < packet.Data.Length)
                {
                    // 当前寄存器地址 = 起始地址 + 索引
                    int currentAddress = startAddress + (byteIndex / 2);

                    // 读取16位原始值（Modbus寄存器默认是16位）
                    short rawValue = BitConverter.ToInt16(packet.Data, byteIndex);

                    // 检查是否在规定的地址映射中
                    if (tempAddressMap.TryGetValue(currentAddress, out var pointInfo))
                    {
                        // 计算物理值（原始值 × 缩放因子）
                        double physicalValue = rawValue * pointInfo.Scale;

                        // 打印解析结果（为后续数据库存储做准备）
                        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] " +
                            $"网关({gatewayIp}) → " +
                            $"{pointInfo.PointName}: {physicalValue}{pointInfo.Unit}, " +
                            $"原始值: {rawValue}, " +
                            $"地址: {currentAddress}");
                    }
                    else
                    {
                        // 未规定的地址，跳过处理
                        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] " +
                            $"网关({gatewayIp}) → 地址{currentAddress}未定义，跳过处理");
                    }

                    byteIndex += 2; // 每个寄存器占2字节
                }
            }
            else
            {
                Console.WriteLine($"网关({gatewayIp})发送了非0x03功能码的数据，暂不处理");
            }

            // 存储数据（同上保持不变）
            lock (_lockObj)
            {
                _receivedGatewayData.Add(new ReceivedGatewayData
                {
                    GatewayId = $"GW-{gatewayIp}",
                    ModbusPacket = packet,
                    RawData = rawData
                });

                // 限制存储数量
                if (_receivedGatewayData.Count > 100)
                    _receivedGatewayData.RemoveRange(0, _receivedGatewayData.Count - 100);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"处理网关数据失败：{ex.Message}");
        }
    }

    /// <summary>
    /// 解析Modbus TCP数据包（客户端侧简化版解析）
    /// </summary>
    private (bool IsValid, ModbusTcpPacket Packet, string Error) ParseModbusPacket(byte[] data, int length)
    {
        try
        {
            if (length < 8)
                return (false, null, "数据包过短");

            var packet = new ModbusTcpPacket
            {
                TransactionId = BitConverter.ToUInt16(data, 0),
                ProtocolId = BitConverter.ToUInt16(data, 2),
                Length = BitConverter.ToUInt16(data, 4),
                UnitId = data[6],
                FunctionCode = data[7]
            };

            // 提取数据区
            packet.Data = new byte[length - 8];
            Array.Copy(data, 8, packet.Data, 0, packet.Data.Length);

            return (true, packet, "解析成功");
        }
        catch (Exception ex)
        {
            return (false, null, ex.Message);
        }
    }

    /// <summary>
    /// 判断是否为文本消息（用于区分服务端响应和网关二进制数据）
    /// </summary>
    private bool IsTextMessage(byte[] data, int length)
    {
        // 文本消息通常只包含可打印字符（ASCII范围）
        for (int i = 0; i < length; i++)
        {
            if (data[i] < 0x20 || data[i] > 0x7E) // 非可打印ASCII字符
                return false;
        }
        return true;
    }

    /// <summary>
    /// 断开连接
    /// </summary>
    private void Disconnect()
    {
        lock (_lockObj)
        {
            if (_isConnected)
            {
                _networkStream?.Dispose();
                _tcpClient?.Close();
                _isConnected = false;
                Console.WriteLine("已断开连接");
            }
        }
    }

    /// <summary>
    /// 清理资源
    /// </summary>
    public override void Dispose()
    {
        _cts.Cancel();
        Disconnect();
        base.Dispose();
    }
}
